import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;
import java.util.Random; 



import pluginUtils.limCircle;
import pluginUtils.limCompareCircle;
import pluginUtils.limComplexe;
import pluginUtils.limConstants;
import pluginUtils.limMatrix;

// test


public class AAAALim extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private ArrayList<limCircle> circleStack=new ArrayList<limCircle>(); 
	private ArrayList<limMatrix> matrixStack=new ArrayList<limMatrix>(); 
	private ArrayList<limCircle> finalStack=new ArrayList<limCircle>(); 
	private int NSTACK=0; 
	private int NFINAL=0; 
	
	
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	
	
	private PrintStream output; 
	private Random generator=new Random(); // One always needs some randomness
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private String[] palette={"Yellow","Red","Blue","Green","White","NavyBlue","Turquoise","Orange"};
	private Color[] palette1={Color.YELLOW,Color.RED,Color.BLUE,Color.GREEN,Color.MAGENTA,Color.ORANGE};
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	
	
	private static int number=0; 
	
	private void drawColoredCircle(limCircle c,int i){
		if(c.getRadius()<=0){
			drawColoredLine(c,i); 
			return; 
		}
		double xCenter=c.getCenter().getReal(); 
		double yCenter=c.getCenter().getIm(); 
		//if(c.getRadius()<0.001) return ; 
		
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		
		primeIP.setColor(palette1[i]); 
		primeIP.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);
		//if(c.getRadius()>5) return ; 
		/*
		String s="sphere{\n<"+xCenter+","+yCenter+",0>,";
		s+=c.getRadius(); 
		s+=" texture{pigment {color "+palette[i]+" filter 0.5}} finish {reflection 0.5 specular 0.7} }"; 
		//output.println(s);
		 */
		  
		number++; 
		/*
		double Xs=c.getCenter().getReal(); 
		double Ys=c.getCenter().getIm(); 
		double rapp=Xs*Xs+Ys*Ys+1; 
		double u=Xs/rapp; 
		double v=Ys/rapp; 
		double w=(Xs*Xs+Ys*Ys)/rapp;
		
		double Xsr=Xs; 
		double Ysr=Ys+c.getRadius(); 
		double rappr=Xsr*Xsr+Ysr*Ysr+1; 
		double ur=Xsr/rappr; 
		double vr=Ysr/rappr; 
		double wr=(Xsr*Xsr+Ysr*Ysr)/rappr;
		
		double nrad=(u-ur)*(u-ur)+(v-vr)*(v-vr)+(w-wr)*(w-wr);
		nrad=Math.sqrt(nrad); 
		
		String s="sphere{<"+u+","+v+","+w+">,";
		//String s="sphere{<"+c.getXcenter()*X+",0,"+c.getYcenter()*Y+">,";
		//s+=c.getRadius()*X;
		s+=nrad; 
		s+=" texture{Texture"+i+"}finish{Finish"+i+"}}"; 
		if(nrad<0.3)
		output.println(s);
		*/
		
	}
	
	private void drawColoredLine(limCircle c,int i){
		double vx=Math.cos(-Math.PI*c.getRadius());
		double vy=Math.sin(-Math.PI*c.getRadius());
		double cx=c.getCenter().getReal(); 
		double cy=c.getCenter().getIm(); 
		primeIP.setColor(palette1[i]);
		drawLine(cx-vx,cy-vy,cx+vx,cy+vy); 
		
	}
	
	private void drawPoint(limComplexe c){
		double xCenter=c.getReal(); 
		double yCenter=c.getIm(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		primeIP.fillOval(intX-10,intY-10,20,20);
	}
	
	
	
	
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	
	private void pushCircle(limCircle c){
		if ((c.getRadius()<limConstants.EPS)&&(c.getRadius()>0)) return; 
		if((c.getRadius()>limConstants.HUGENESS)||(c.getCenter().abs()>limConstants.HUGENESS)) return; 
		if(NSTACK==limConstants.MAXC) return;
		System.out.println(c+" added"); 
		circleStack.add(c); 
		NSTACK++; 
		return; 
	}
	
	private void evolve(limCircle c){
		for(limMatrix m:matrixStack)
			pushCircle(m.imageCircle(c)); 
	}
	
	private void evolveStack(){
		System.out.println("evolveStack "+generation);
		int nold=NSTACK; 
		for(int i=0;i<nold;i++) evolve(circleStack.get(i));
	}
	
	
	private void sortCircles(ArrayList<limCircle> st){
		Collections.sort(st,new limCompareCircle());
	}
	
	
	// TODO qu'est-ce qu'on fabrique dans cette fonction ? 
	private boolean mergeCircles(){
		ArrayList<limCircle> temp=new ArrayList<limCircle>(); 
		uniqueCircles(circleStack,true); 
		
		
		if(NSTACK+NFINAL>limConstants.MAXC)
			return true;
		
		for(limCircle c:circleStack)
			if(!finalStack.contains(c)) {
				finalStack.add(c);
				temp.add(c); 
			}
		circleStack=new ArrayList<limCircle>(temp); 
		NSTACK=circleStack.size(); 
		NFINAL=finalStack.size();
		
		
		sortCircles(finalStack); 
		return false; 
	}// mergeCircles
	
	
	
	// which=false : finalStack
	// which=true : circleStack
	private void uniqueCircles(ArrayList<limCircle> st,boolean which){
		double tailleStack; 
		ArrayList<limCircle> temp=new ArrayList<limCircle>(); 
		if(which){
			tailleStack=NSTACK; 
			if(NSTACK<2) return; 
		}
		else{
			tailleStack=NFINAL; 
			if(NFINAL<2) return; 
		}
		
		sortCircles(st); 
		
		
		int i=1; 
		int n=1; 
		temp.add(st.get(0));
		for(limCircle c:st){
			if(!temp.contains(c)) temp.add(c); 
		}
		
		if(which) {
			NSTACK=n;
			circleStack=new ArrayList<limCircle>(temp); 
		}
		else {
			NFINAL=n;
			finalStack=new ArrayList<limCircle>(temp);
		}
		
	
	}// uniqueCircles
	
	private int generation=-1;
	private void generate(){
		
		for(limCircle c:circleStack) 
			finalStack.add(c); 
		NFINAL=NSTACK; 
		uniqueCircles(finalStack,false);
		System.out.println("final stack :"+NFINAL); 
		int i; 
		for( i=0;i<limConstants.DEPTH;i++){
			generation++; 
			System.out.println("generation "+(generation)); 
			
			if(NSTACK==0){
				System.out.println("Generation complete "+generation); 
				break; 
			}
			evolveStack(); 
			if(mergeCircles()){
				System.out.println("Stack FULL !"); 
				break; 
			}
			
		} // for i
		if(i>limConstants.DEPTH)
			System.out.println("Depth attained"); 
	}// generate
	
	
	
	public void run(String arg) {
		primeIP = new ColorProcessor(X,Y);
		
		
		double limit=5;
	
		setBounds(-limit,limit,-limit,limit);
		
		setCoefs(); 
		
		
		
		
		
		
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(5); 
		
		
		
		
		setProcessor("Verifying", primeIP);
		
		drawLine(x0,0,x1,0);
		drawLine(0,y0,0,y1);
		
		pushCircle(new limCircle(limComplexe.ZERO,1.0));
		matrixStack.add(new limMatrix(limComplexe.createComplexeFromRI(1,1),
				limComplexe.createComplexeFromRI(0,1),
				limComplexe.createComplexeFromRI(0,-1),
				limComplexe.createComplexeFromRI(1,-1)).make_s12()
		);
		matrixStack.add(new limMatrix(limComplexe.createComplexeFromRI(1,-1),
				limComplexe.createComplexeFromRI(0,-1),
				limComplexe.createComplexeFromRI(0,1),
				limComplexe.createComplexeFromRI(1,1)).make_s12()
		);
		matrixStack.add(new limMatrix(limComplexe.createComplexeFromRI(0.955,-0.025),
				limComplexe.createComplexeFromRI(0.045,0.025),
				limComplexe.createComplexeFromRI(-1.955,0.025),
				limComplexe.createComplexeFromRI(0.955,-0.025)).make_s12()
		);
		matrixStack.add(new limMatrix(limComplexe.createComplexeFromRI(0.955,-0.025),
				limComplexe.createComplexeFromRI(-0.045,-0.025),
				limComplexe.createComplexeFromRI(1.955,-0.025),
				limComplexe.createComplexeFromRI(0.955,-0.025)).make_s12()
		);
		
		//for(int i=0;i<4;i++) drawColoredCircle(matrixStack.get(i).matrixToCircle(),3); 
		
		
		
			limCircle c=new limCircle(limComplexe.createComplexeFromRI(-1,0),1); 
			System.out.println(c); 
			drawColoredCircle(c, 3); 
			limCircle c1=matrixStack.get(3).imageCircle(c); 
			drawColoredCircle(matrixStack.get(3).matrixToCircle(),5); 
			drawColoredCircle(c1, 2); 
		
		
		
		
		
		
		
		
		show();
		System.out.println("done "+number); 
	}	
}
	